/**
 * JSparrow - Une version numérique du jeu "Attention Pirates !" - Copyright (C) 2014 Black Pearl
 *
 * This program is free software: you can redistribute it and/or modify it under the terms of the
 * GNU General Public License as published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
 * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with this program. If
 * not, see <http://www.gnu.org/licenses/>.
 */
package App.Modele;

import App.main;

import java.awt.Point;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Random;

/**
 * Classe de gestion du plateau de jeu.
 *
 * @author BlackPearl
 */
public class Plateau implements Serializable {

    /**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private Case cases[][];
    private ArrayList<Case> listeCases;
    private ArrayList<Bateau> listeBateaux;
    private ArrayList<Repaire> listeRepaires;

    /**
     * Initalisation du contexte.
     *
     * @param listeBateaux liste des bateaux du plateau
     */
    public Plateau(ArrayList<Bateau> listeBateaux) {
        this.cases = new Case[9][10];
        this.listeCases = new ArrayList<Case>();
        this.listeBateaux = listeBateaux;
        this.listeRepaires = new ArrayList<Repaire>();

        // This piece of code is to instantiate the Plateau's cases and to store these cases into the list.
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 10; j++) {
                if (((i % 2 == 0 && j % 2 == 0) || (i % 2 != 0 && j % 2 != 0)) && !(Configuration.EmplacementsInterdits.contains(new Point(i, j)))) {
                    this.cases[i][j] = new Case(i, j);
                    this.listeCases.add(this.cases[i][j]);
                } else {
                    this.cases[i][j] = null;
                }
            }
        }

        // This piece of code is to initialize the Bateaux to the first case(0,0);
        this.cases[0][0].getEntites().addAll(listeBateaux);

        // This piece of code is to place the Repaires.
        long seed = System.nanoTime();
        int indexOfPoints = 0;
        ArrayList<Repaire> initList = new ArrayList<Repaire>(Configuration.ModelesRepaires);
        Collections.shuffle(initList, new Random(seed));
        for (Point p : Configuration.EmplacementsRepaires) {
            if (cases[p.x][p.y] != null) {
                cases[p.x][p.y].getEntites().add(initList.get(indexOfPoints));
            }
            indexOfPoints++;
        }

    }
    

    /**
     * Obtenir la liste des repaires.
     *
     * @return liste des repaires du plateau
     */
    public ArrayList<Repaire> getListeRepaires() {
        return this.listeRepaires;
    }

    /**
     * Définir la liste des repaires.
     *
     * @param listeRepaires liste des repaires du plateau
     */
    public void setListeRepaires(ArrayList<Repaire> listeRepaires) {
        this.listeRepaires = listeRepaires;
    }

    /**
     * Déplacer un bateau sur le repaire.
     *
     * @param bateau le bateau à déplacer
     * @param p le nouvel emplacement du bateau
     * @return true si le bateau est déplacé ou false sinon
     */
    public boolean deplacerBateau(Bateau bateau, Point p) {
        if (getDeplacementPossible(bateau).contains(p)) {
            /*
             * TODO: Move the boat.
             */
            this.cases[bateau.getEmplacement().x][bateau.getEmplacement().y].getEntites().remove(bateau);
            this.cases[p.x][p.y].getEntites().add(bateau);
            bateau.deplacer(p);
            return true;
        }
        return false;
    }

    /**
     * Lister les déplacements possibles d'un bateau.
     *
     * @param b le bateau concerné
     * @return la liste des déplacements possibles
     */
    public ArrayList<Point> getDeplacementPossible(Bateau b) {
    	System.out.println("BateauDEPL:" + b.getDeplacementsPossibles());
        ArrayList<Point> depl = b.getDeplacementsPossibles();
        ArrayList<Point> result = new ArrayList<Point>();
        
        for (Point p : depl) {
            int xPoint = p.x;
            int yPoint =  p.y;
            if (xPoint >= 0 && xPoint < 9 && yPoint >= 0 && yPoint < 10
                    && (cases[xPoint][yPoint] != null)) {
                ArrayList<EntiteCase> ents = cases[xPoint][yPoint].getEntites();
                if (ents.size() == 0 || !(ents.get(0) instanceof Repaire)) {
                    result.add(new Point(xPoint, yPoint ));
                }

            }
        }
        return result;
    }

    public void isPieceAttaquable() {
        /**
         * @todo Implémenter.
         */
    }

    /**
     * Obtenir la liste des pièces attaquables par un bateau.
     *
     * @param bateau le bateau concerné
     * @return la liste des pièces attaquables
     */
    public ArrayList<EntiteCase> getPiecesAttaquables(Bateau bateau) {
        ArrayList<EntiteCase> piecesAttaquables = new ArrayList<EntiteCase>();

        /*
         * TODO: Check if x & y do not override tab dimensions.
         */
        if (this.cases[bateau.getEmplacement().x][bateau.getEmplacement().y] != null) {
            for (EntiteCase ent : this.cases[bateau.getEmplacement().x][bateau.getEmplacement().y].getEntites()) {
                if (((Bateau) ent).isAttaquable()) {
                    piecesAttaquables.add(ent);
                }
            }
        }

        /*
         * TODO: Check if x & y do not override tab dimensions.
         */
        for (Point point : Configuration.ModeleAttaqueRepaire) {
            if (bateau.getEmplacement().x + point.x >= 0 && bateau.getEmplacement().x < 9 && bateau.getEmplacement().y >= 0 && bateau.getEmplacement().y < 10
                    && this.cases[bateau.getEmplacement().x + point.x][bateau.getEmplacement().y + point.y] != null) {
                if (this.cases[bateau.getEmplacement().x + point.x][bateau.getEmplacement().y + point.y].getEntites().size() == 1) {
                    EntiteCase ent = this.cases[bateau.getEmplacement().x + point.x][bateau.getEmplacement().y + point.y].getEntites().get(0);
                    if (ent instanceof Repaire) {
                        if (ent.isAttaquable()) {
                            piecesAttaquables.add(ent);
                        }
                    }
                }
            }
        }
        piecesAttaquables.remove(bateau);
        return piecesAttaquables;

    }

    /**
     * Vérifier qu'un bateau peut attaquer une entité.
     *
     * @param attaquant le bateau concerné
     * @param defenseur l'entité attaquée
     * @return true si l'attaquant peut attaquer le défenseur ou false s'il ne peut pas (pas assez
     * de pirates/canons ou pas assez près)
     */
    public boolean checkAttaque(Bateau attaquant, EntiteCase defenseur) {

        if (attaquant.peutAttaquer()) {
            // Si la pièce peut attaquer, on regarde si elle peut attaquer spécifiquement le défenseur
            return (this.getPiecesAttaquables(attaquant).contains(defenseur));
        } else {
            // Si la pièce ne peut pas attaquer, on retourne false directement
            return false;
        }
    }

    public void getTypeEntite() {
        /**
         * @TODO Implémenter.
         */
    }

    /**
     * Attaquer une entité.
     *
     * @param b1 le bateau attaquant
     * @param cible l'entité attaquée
     * @return true si l'attaque réussie ou false sinon
     */
    public boolean attaquer(Bateau b1, EntiteCase cible) {
        if (checkAttaque(b1, cible)) {
            int nbDesLances = (b1.getNbPirates() > 0 ? b1.getNbCanons() : 0);

            // Attaque
            for (int i = 0; i < nbDesLances; i++) {
                int lanceDe = Configuration.random(0, 5);
                cible.recoitAttaque(lanceDe);
            }

            // Riposte
            if (cible.peutAttaquer() && b1.isAttaquable()) {
                for (int i = 0; i < nbDesLances; i++) {
                    int lanceDe = Configuration.random(0, 5);
                    b1.recoitAttaque(lanceDe);
                }
            }
            return true;

        }
        return false;
    }

    /**
     * Vérifier qu'un déplacement est possible pour un bateau.
     *
     * @param destination le nouvel emplacement
     * @param bateau le bateau concerné
     * @return true si le deplacement du bateau est possible dans la case de destination ou false
     * sinon
     */
    public boolean checkDeplacement(Point destination, Bateau bateau) {
    	System.out.println("DEPL-POSSIBLES =>  " + this.getDeplacementPossible(bateau));
        return (this.getDeplacementPossible(bateau).contains(destination));
    }

    /**
     * Obtenir le nombre de crédits possibles pour la réparation d'un bateau.
     *
     * @param bateau le bateau concerné
     * @return le nombre de crédits pour réparer le bateau (6 crédits si on est dans un port ou 2
     * sinon)
     */
    public int getNbCreditReparation(Bateau bateau) {
        Case caseBateau = getCaseBateau(bateau);
        if (caseBateau == cases[0][0] || caseBateau == cases[1][1] || caseBateau == cases[0][2]) {
            return 6;
        }
        return 2;
    }

    /**
     * Obtenir la case sur laquelle se trouve le bateau donné.
     *
     * @param bateau le bateau concerné
     * @return la case sur laquelle se trouve le bateau
     */
    public Case getCaseBateau(Bateau bateau) {
        return (this.cases[bateau.getEmplacement().x][bateau.getEmplacement().y]);
    }

    /**
     * Obtenir la case présent aux coordonnées données.
     *
     * @param coordonnee emplacement
     * @return la case demandée
     */
    public Case getCase(Point coordonnee) {
        return (this.cases[coordonnee.x][coordonnee.y]);
    }

    /**
     * Obtenir le repaire présent aux coordonnées données.
     *
     * @param coordonnees emplacement du repaire
     * @return repaire s'il existe ou null sinon
     */
    public Repaire getRepaire(Point coordonnees) {
        if (this.cases[coordonnees.x][coordonnees.y] != null) {
            ArrayList<EntiteCase> listEntiteCase = this.cases[coordonnees.x][coordonnees.y].getEntites();
            for (EntiteCase ent : listEntiteCase) {
                if (ent instanceof Repaire) {
                    return (Repaire) ent;
                }
            }
        }
        return null;
    }

    /**
     * Obtenir la liste des bateaux du plateau.
     *
     * @return liste des bateaux
     */
    public ArrayList<Bateau> getListeBateaux() {
        return listeBateaux;
    }

    /**
     * @TODO [DEBUG] Supprimer quand plus utilisé
     */
    public void DEBUG_afficherPlateau() {
        for (int yPos = 0; yPos < 10; yPos++) {
            if (yPos % 2 != 0) {
                System.out.print("  ");
            }
            for (int xPos = 0; xPos < 9; xPos++) {
                if (this.cases[xPos][yPos] != null) {
                    System.out.print(this.cases[xPos][yPos].getEntites());
                }
                System.out.print(" ");
            }
            System.out.println();

        }
    }
    
    public String toCom(){
    	ArrayList<Bateau> listeBateau = this.getListeBateaux();
    	StringBuilder listeBateauStringBuffer = new StringBuilder();
    	for(int i = 0; i < listeBateau.size(); i++){
    		listeBateauStringBuffer.append("("+listeBateau.get(i).getEmplacement().x+";"+listeBateau.get(i).getEmplacement().y+")");
    		if(i+1 < listeBateau.size()){
    			listeBateauStringBuffer.append("-");
    		}
    	}
 
        return listeBateauStringBuffer.toString();
    	
    }

}
